﻿#ifndef YUTOPP_OAUTH_BASIC_AUTHENTICATOR_1_HPP
#define YUTOPP_OAUTH_BASIC_AUTHENTICATOR_1_HPP

#include "../../../encoding.hpp"
#include "../../../url_base64.hpp"

#include "../../detail/base.hpp"
#include "sync_part.hpp"
#include "async_part.hpp"

#include <boost/assign.hpp>

namespace webapp
{
	namespace oauth
	{
		namespace detail
		{
			namespace v1
			{
				template<typename Protocol, typename Derived, typename Policy>
				struct switch_part
					: boost::mpl::if_<
						protocol::is_sync_connection<Protocol>,
						sync_part<Derived, Policy>,
						authenticator_async_part<Derived, Policy>
					>
				{};
			}
		}

		//現時点では非同期専用・・・
		template<typename Derived, typename Protocol, typename Policy>
		class basic_authenticator<
			Derived,
			Protocol,
			Policy,
			typename boost::enable_if<
				is_v1<typename Policy::auth_type>
			>::type
		>
			: public detail::v1::switch_part<Protocol, basic_authenticator<Derived, Protocol, Policy>, Policy>::type
		{
			friend detail::v1::switch_part<Protocol, basic_authenticator<Derived, Protocol, Policy>, Policy>::type;

			typedef typename Policy::auth_type				auth_type;
			typedef typename Policy::translator_type		translator;

		public:
			typedef typename auth_type::key_type			auth_key_type;
			typedef typename Policy::string_type			string_type;
			typedef std::string								binary_type;
			typedef typename detail::return_val<
				Protocol, binary_type
			>::type			return_type;

			typedef typename Policy::optional_request_token	optional_request_token;
			typedef typename Policy::optional_access_token	optional_access_token;

			auth_key_type get_auth_key() const
			{
				return auth_.get_key();
			}

			bool is_complete() const
			{
				return auth_.get_key().is_complete();
			}

			binary_type get_authorize_url( const binary_type& token ) const
			{
				const binary_type path = binary_type( Policy::authorize_path() ) + "?oauth_token=" + url::encode( token );
				return binary_type( Protocol::name() ) + "://" + Policy::get_domain() + path;
			}

		protected:
			explicit basic_authenticator( const auth_key_type& key )
				: auth_( key )
			{}

			void set_token( const binary_type& oauth_token, const binary_type& oauth_token_secret )
			{
				auth_.set_token( oauth_token, oauth_token_secret );
			}

			//~basic_authenticator() {}

		private:
			typedef
				std::tuple<binary_type, binary_type, binary_type>
			address_type;

			address_type get_request_address() const
			{
				// to do - urlのエンコード処理

				const binary_type url = binary_type( Protocol::name() ) + "://" + Policy::get_domain() + Policy::request_path();
				return address_type( Policy::request_method(), Policy::request_path(), url );
			}

			address_type get_access_address() const
			{
				// to do - urlのエンコード処理

				const binary_type url = binary_type( Protocol::name() ) + "://" + Policy::get_domain() + Policy::access_path();
				return address_type( Policy::access_method(), Policy::access_path(), url );
			}

			//
			std::map<binary_type, binary_type>
				make_request(
					const binary_type& auth_header
					) const
			{
				return boost::assign::map_list_of<binary_type, binary_type>
					( "Host", Policy::get_domain() )
					( "Accept-Charset", translator::encoder_type::name() )
					( "User-Agent", Policy::get_user_agent() )
					( "Content-type", "application/x-www-form-urlencoded" )
					( "Authorization", auth_header )
					( "Content-Length", "0" )
					( "Connection", "close" );
			}

			template<class Handler>
			return_type send_request(
				const binary_type& method,
				const binary_type& path,
				const binary_type& auth_header,
				Handler& handler )
			{
				return sender_(
					Policy::get_domain(),
					method,
					path,
					make_request( auth_header ),
					"",
					handler
					);
			}
			
			template<typename Handler>
			return_type send_request_token_request(
				const string_type& extension,
				Handler& handler )
			{
				const address_type address( get_request_address() );

				return send_request(
							std::get<0>( address ),
							std::get<1>( address ),
							auth_.make_header_for_request_token(
								std::get<0>( address ),
								std::get<2>( address ),
								util::parse_url_encoded_text( extension )
									| boost::adaptors::transformed( &basic_authenticator::encode_pair )
								),
							handler
							);
			}

			template<typename Handler>
			return_type send_access_token_request(
				const binary_type& pin,
				const binary_type& oauth_token,
				const binary_type& oauth_token_secret,
				Handler& handler )
			{
				const address_type address( get_access_address() );
				translator::encoder_type e;

				return send_request(
							std::get<0>( address ),
							std::get<1>( address ),
							auth_.make_header_for_access_token(
								std::get<0>( address ),
								std::get<2>( address ),
								e( pin ),
								e( oauth_token ),
								e( oauth_token_secret )
								),
							handler
							);
			}

			optional_request_token parse_request_response( const binary_type& data ) const
			{
				return static_cast<const Derived&>(*this).parse_request_token( data );
			}
			optional_access_token parse_access_response( const binary_type& data )
			{
				return static_cast<Derived&>(*this).parse_access_token( data );
			}

			static std::pair<binary_type, binary_type>
				encode_pair( const std::pair<binary_type, binary_type>& t )
			{
				translator::encoder_type e;
				return std::make_pair( url::encode( e( t.first ) ), url::encode( e( t.second ) ) );
			}

			auth_type auth_;	// 認証
			Protocol sender_;	// 通信
		};
	} // - oauth
} // - webapp

#endif /*YUTOPP_OAUTH_BASIC_AUTHENTICATOR_1_HPP*/
